package com.permissions.dispatcher.processor;

import static com.permissions.dispatcher.processor.util.Constants.GEN_CLASS_SUFFIX;
import static com.permissions.dispatcher.processor.util.Helpers.typeMirrorOf;

import com.permissions.dispatcher.annotation.NeedsPermission;
import com.permissions.dispatcher.annotation.OnNeverAskAgain;
import com.permissions.dispatcher.annotation.OnPermissionDenied;
import com.permissions.dispatcher.annotation.OnShowRationale;
import com.permissions.dispatcher.processor.util.Extensions;
import com.permissions.dispatcher.processor.util.Helpers;
import com.permissions.dispatcher.processor.util.Validators;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeVariableName;

import java.util.ArrayList;
import java.util.List;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;

/**
 * 被注解类核心Element文件
 */
public class RuntimePermissionsElement {
    /**
     * 类型方法块
     */
    public TypeElement element;

    private TypeName typeName;

    private List<TypeVariableName> typeVariables;

    private String packageName;

    private String inputClassName;

    private String generatedClassName;

    /**
     * 注解执行需要一个或多个权限操作集合
     */
    public List<ExecutableElement> needsElements;
    /**
     * Rationale集合
     */
    public List<ExecutableElement> onRationaleElements;
    /**
     * 已拒绝集合
     */
    public List<ExecutableElement> onDeniedElements;
    /**
     * 被拒绝
     */
    public List<ExecutableElement> onNeverAskElements;

    public RuntimePermissionsElement(TypeElement element) {
        this.element = element;
        typeName = TypeName.get(element.asType());
        typeVariables = new ArrayList<>();
        onRationaleElements = new ArrayList<>();
        onDeniedElements = new ArrayList<>();
        onNeverAskElements = new ArrayList<>();
        for (TypeParameterElement element1 : element.getTypeParameters()) {
            typeVariables.add(TypeVariableName.get(element1));
        }
        packageName = Extensions.packageName(element);
        inputClassName = Extensions.simpleString(element);
        generatedClassName = inputClassName + GEN_CLASS_SUFFIX;
        needsElements = Extensions.childElementsAnnotatedWith(element, NeedsPermission.class);
        onRationaleElements = Extensions.childElementsAnnotatedWith(element, OnShowRationale.class);
        onDeniedElements = Extensions.childElementsAnnotatedWith(element, OnPermissionDenied.class);
        onNeverAskElements = Extensions.childElementsAnnotatedWith(element, OnNeverAskAgain.class);
        validateNeedsMethods();
        validateRationaleMethods();
        validateDeniedMethods();
        validateNeverAskMethods();
    }

    private void validateNeverAskMethods() {
        Validators.checkNotEmpty(needsElements, this, NeedsPermission.class);
        Validators.checkPrivateMethods(needsElements, NeedsPermission.class);
        Validators.checkMethodSignature(needsElements);
        Validators.checkMixPermissionType(needsElements, NeedsPermission.class);
        Validators.checkDuplicatedMethodName(needsElements);
    }

    private void validateDeniedMethods() {
        Validators.checkDuplicatedValue(onRationaleElements, OnShowRationale.class);
        Validators.checkPrivateMethods(onRationaleElements, OnShowRationale.class);
        Validators.checkMethodSignature(onRationaleElements);
        Validators.checkMethodParameters(
                onRationaleElements, 1, typeMirrorOf("com.permissions.dispatcher.annotation.PermissionRequest"));
    }

    private void validateRationaleMethods() {
        Validators.checkDuplicatedValue(onDeniedElements, OnPermissionDenied.class);
        Validators.checkPrivateMethods(onDeniedElements, OnPermissionDenied.class);
        Validators.checkMethodSignature(onDeniedElements);
        Validators.checkMethodParameters(onDeniedElements, 0, null);
    }

    private void validateNeedsMethods() {
        Validators.checkDuplicatedValue(onNeverAskElements, OnNeverAskAgain.class);
        Validators.checkPrivateMethods(onNeverAskElements, OnNeverAskAgain.class);
        Validators.checkMethodSignature(onNeverAskElements);
        Validators.checkMethodParameters(onNeverAskElements, 0, null);
        Validators.checkSpecialPermissionsWithNeverAskAgain(onNeverAskElements, OnNeverAskAgain.class);
    }

    /**
     * findOnRationaleForNeeds
     *
     * @param needsElement needsElement
     * @return ExecutableElement
     */
    public ExecutableElement findOnRationaleForNeeds(ExecutableElement needsElement) {
        return Helpers.findMatchingMethodForNeeds(needsElement, onRationaleElements, OnShowRationale.class);
    }

    /**
     * findOnDeniedForNeeds
     *
     * @param needsElement needsElement
     * @return ExecutableElement
     */
    public ExecutableElement findOnDeniedForNeeds(ExecutableElement needsElement) {
        return Helpers.findMatchingMethodForNeeds(needsElement, onDeniedElements, OnPermissionDenied.class);
    }

    /**
     * getNeedsElements
     *
     * @return List<ExecutableElement>
     */
    public List<ExecutableElement> getNeedsElements() {
        return needsElements;
    }

    public void setNeedsElements(List<ExecutableElement> needsElements) {
        this.needsElements = needsElements;
    }

    public List<ExecutableElement> getOnRationaleElements() {
        return onRationaleElements;
    }

    public void setOnRationaleElements(List<ExecutableElement> onRationaleElements) {
        this.onRationaleElements = onRationaleElements;
    }

    public List<ExecutableElement> getOnDeniedElements() {
        return onDeniedElements;
    }

    public void setOnDeniedElements(List<ExecutableElement> onDeniedElements) {
        this.onDeniedElements = onDeniedElements;
    }

    public List<ExecutableElement> getOnNeverAskElements() {
        return onNeverAskElements;
    }

    public void setOnNeverAskElements(List<ExecutableElement> onNeverAskElements) {
        this.onNeverAskElements = onNeverAskElements;
    }

    public TypeElement getElement() {
        return element;
    }

    public void setElement(TypeElement element) {
        this.element = element;
    }

    public TypeName getTypeName() {
        return typeName;
    }

    public void setTypeName(TypeName typeName) {
        this.typeName = typeName;
    }

    public List<TypeVariableName> getTypeVariables() {
        return typeVariables;
    }

    public void setTypeVariables(List<TypeVariableName> typeVariables) {
        this.typeVariables = typeVariables;
    }

    public String getPackageName() {
        return packageName;
    }

    public void setPackageName(String packageName) {
        this.packageName = packageName;
    }

    public String getInputClassName() {
        return inputClassName;
    }

    public void setInputClassName(String inputClassName) {
        this.inputClassName = inputClassName;
    }

    public String getGeneratedClassName() {
        return generatedClassName;
    }

    /**
     * 授权类名
     * @param generatedClassName name
     */
    public void setGeneratedClassName(String generatedClassName) {
        this.generatedClassName = generatedClassName;
    }

    /**
     * 拒绝方法块findOnNeverAskForNeeds
     * @param needsMethod 方法
     * @return ExecutableElement
     */
    public ExecutableElement findOnNeverAskForNeeds(ExecutableElement needsMethod) {
        return Helpers.findMatchingMethodForNeeds(needsMethod, onNeverAskElements, OnNeverAskAgain.class);
    }
}
